var _useCapture = false;

/**
DOM_VK_BACK_SPACE : 8
DOM_VK_TAB : 9
DOM_VK_RETURN : 13
DOM_VK_SPACE : 32
DOM_VK_PAGE_UP : 33
DOM_VK_PAGE_DOWN : 34
DOM_VK_END : 35
DOM_VK_HOME : 36
DOM_VK_LEFT : 37
DOM_VK_UP : 38
DOM_VK_RIGHT : 39
DOM_VK_DOWN : 40
DOM_VK_DELETE : 46
DOM_VK_0 ~ DOM_VK_9 : 48 ~ 57
DOM_VK_SEMICOLON : 59 (;:)
DOM_VK_EQUALS : 61 (=+) (+)
DOM_VK_A ~ DOM_VK_Z : 65 ~ 90
DOM_VK_MULTIPLY : 106 (*)
DOM_VK_SUBTRACT : 109 (-_) (-)
DOM_VK_DECIMAL : 110 (.)
DOM_VK_DIVIDE : 111 (/)
DOM_VK_COMMA : 188 (,<)
DOM_VK_PERIOD : 190 (.>)
DOM_VK_SLASH : 191 (/?)
DOM_VK_BACK_QUOTE : 192 (`~)
DOM_VK_OPEN_BRACKET : 219 ([{)
DOM_VK_BACK_SLASH : 220 (\|)
DOM_VK_CLOSE_BRACKET : 221 (]})
DOM_VK_QUOTE : 222 ('")
*/
// 输入文字的键值
var _INPUT_KEY_MAP = _toMap(
  "8,9,13,32,46,48..57,59,61,65..90,106,109..111,188,190..192,219..222"
);
// 移动光标的键值
var _CURSORMOVE_KEY_MAP = _toMap("33..40");
// 输入文字或移动光标的键值
var _CHANGE_KEY_MAP = {};
_each(_INPUT_KEY_MAP, function(key, val) {
  _CHANGE_KEY_MAP[key] = val;
});
_each(_CURSORMOVE_KEY_MAP, function(key, val) {
  _CHANGE_KEY_MAP[key] = val;
});

// add native event
function _bindEvent(el, type, fn) {
  if (el.addEventListener) {
    el.addEventListener(type, fn, _useCapture);
  } else if (el.attachEvent) {
    el.attachEvent("on" + type, fn);
  }
}
// remove native event
function _unbindEvent(el, type, fn) {
  if (el.removeEventListener) {
    el.removeEventListener(type, fn, _useCapture);
  } else if (el.detachEvent) {
    el.detachEvent("on" + type, fn);
  }
}

var _EVENT_PROPS = (
  "altKey,attrChange,attrName,bubbles,button,cancelable,charCode,clientX,clientY,ctrlKey,currentTarget," +
  "data,detail,eventPhase,fromElement,handler,keyCode,metaKey,newValue,offsetX,offsetY,originalTarget,pageX," +
  "pageY,prevValue,relatedNode,relatedTarget,screenX,screenY,shiftKey,srcElement,target,toElement,view,wheelDelta,which"
).split(",");

// create KEvent class
function KEvent(el, event) {
  this.init(el, event);
}
_extend(KEvent, {
  init: function(el, event) {
    var self = this,
      doc = el.ownerDocument || el.document || el;
    self.event = event;
    _each(_EVENT_PROPS, function(key, val) {
      self[val] = event[val];
    });
    if (!self.target) {
      self.target = self.srcElement || doc;
    }
    if (self.target.nodeType === 3) {
      self.target = self.target.parentNode;
    }
    if (!self.relatedTarget && self.fromElement) {
      self.relatedTarget =
        self.fromElement === self.target ? self.toElement : self.fromElement;
    }
    if (self.pageX == null && self.clientX != null) {
      var d = doc.documentElement,
        body = doc.body;
      self.pageX =
        self.clientX +
        ((d && d.scrollLeft) || (body && body.scrollLeft) || 0) -
        ((d && d.clientLeft) || (body && body.clientLeft) || 0);
      self.pageY =
        self.clientY +
        ((d && d.scrollTop) || (body && body.scrollTop) || 0) -
        ((d && d.clientTop) || (body && body.clientTop) || 0);
    }
    if (
      !self.which &&
      (self.charCode || self.charCode === 0 ? self.charCode : self.keyCode)
    ) {
      self.which = self.charCode || self.keyCode;
    }
    if (!self.metaKey && self.ctrlKey) {
      self.metaKey = self.ctrlKey;
    }
    if (!self.which && self.button !== undefined) {
      self.which =
        self.button & 1 ? 1 : self.button & 2 ? 3 : self.button & 4 ? 2 : 0;
    }
    /**
			DOM_VK_SEMICOLON : 59 (;:)
				- IE,WEBKIT: 186
				- GECKO,OPERA : 59
			DOM_VK_EQUALS : 61 (=+)
				- IE,WEBKIT : 187
				- GECKO : 107
				- OPERA : 61
			DOM_VK_NUMPAD0 ~ DOM_VK_NUMPAD9 : 96 ~ 105
				- IE、WEBKIT,GECKO : 96 ~ 105
				- OPERA : 48 ~ 57
			DOM_VK_MULTIPLY : 106 (*)
				- IE、WEBKIT,GECKO : 106
				- OPERA : 42
			DOM_VK_ADD : 107 (+)
				- IE、WEBKIT,GECKO : 107
				- OPERA : 43
			DOM_VK_SUBTRACT : 109 (-_) (-)
				- IE,WEBKIT : 189, 109
				- GECKO : 109, 109
				- OPERA : 109, 45
			DOM_VK_DECIMAL : 110 (.)
				- IE、WEBKIT,GECKO : 110
				- OPERA : 78
			DOM_VK_DIVIDE : 111 (/)
				- IE、WEBKIT,GECKO : 111
				- OPERA : 47

			Reference:
			https://developer.mozilla.org/en/DOM/Event/UIEvent/KeyEvent
			http://msdn.microsoft.com/en-us/library/ms536940(v=VS.85).aspx
		*/
    switch (self.which) {
      case 186:
        self.which = 59;
        break;
      case 187:
      case 107:
      case 43:
        self.which = 61;
        break;
      case 189:
      case 45:
        self.which = 109;
        break;
      case 42:
        self.which = 106;
        break;
      case 47:
        self.which = 111;
        break;
      case 78:
        self.which = 110;
        break;
    }
    if (self.which >= 96 && self.which <= 105) {
      self.which -= 48;
    }
  },
  preventDefault: function() {
    var ev = this.event;
    if (ev.preventDefault) {
      ev.preventDefault();
    } else {
      ev.returnValue = false;
    }
  },
  stopPropagation: function() {
    var ev = this.event;
    if (ev.stopPropagation) {
      ev.stopPropagation();
    } else {
      ev.cancelBubble = true;
    }
  },
  stop: function() {
    this.preventDefault();
    this.stopPropagation();
  }
});

var _eventExpendo = "kindeditor_" + _TIME,
  _eventId = 0,
  _eventData = {};

function _getId(el) {
  return el[_eventExpendo] || null;
}

function _setId(el) {
  el[_eventExpendo] = ++_eventId;
  return _eventId;
}

function _removeId(el) {
  try {
    delete el[_eventExpendo];
  } catch (e) {
    if (el.removeAttribute) {
      el.removeAttribute(_eventExpendo);
    }
  }
}

function _bind(el, type, fn) {
  if (type.indexOf(",") >= 0) {
    _each(type.split(","), function() {
      _bind(el, this, fn);
    });
    return;
  }
  var id = _getId(el);
  if (!id) {
    id = _setId(el);
  }
  if (_eventData[id] === undefined) {
    _eventData[id] = {};
  }
  var events = _eventData[id][type];
  if (events && events.length > 0) {
    _unbindEvent(el, type, events[0]);
  } else {
    _eventData[id][type] = [];
    _eventData[id].el = el;
  }
  events = _eventData[id][type];
  if (events.length === 0) {
    events[0] = function(e) {
      var kevent = e ? new KEvent(el, e) : undefined;
      _each(events, function(i, event) {
        if (i > 0 && event) {
          event.call(el, kevent);
        }
      });
    };
  }
  if (_inArray(fn, events) < 0) {
    events.push(fn);
  }
  _bindEvent(el, type, events[0]);
}

function _unbind(el, type, fn) {
  if (type && type.indexOf(",") >= 0) {
    _each(type.split(","), function() {
      _unbind(el, this, fn);
    });
    return;
  }
  var id = _getId(el);
  if (!id) {
    return;
  }
  if (type === undefined) {
    if (id in _eventData) {
      _each(_eventData[id], function(key, events) {
        if (key != "el" && events.length > 0) {
          _unbindEvent(el, key, events[0]);
        }
      });
      delete _eventData[id];
      _removeId(el);
    }
    return;
  }
  if (!_eventData[id]) {
    return;
  }
  var events = _eventData[id][type];
  if (events && events.length > 0) {
    if (fn === undefined) {
      _unbindEvent(el, type, events[0]);
      delete _eventData[id][type];
    } else {
      _each(events, function(i, event) {
        if (i > 0 && event === fn) {
          events.splice(i, 1);
        }
      });
      if (events.length == 1) {
        _unbindEvent(el, type, events[0]);
        delete _eventData[id][type];
      }
    }
    var count = 0;
    _each(_eventData[id], function() {
      count++;
    });
    if (count < 2) {
      delete _eventData[id];
      _removeId(el);
    }
  }
}

function _fire(el, type) {
  if (type.indexOf(",") >= 0) {
    _each(type.split(","), function() {
      _fire(el, this);
    });
    return;
  }
  var id = _getId(el);
  if (!id) {
    return;
  }
  var events = _eventData[id][type];
  if (_eventData[id] && events && events.length > 0) {
    events[0]();
  }
}

function _ctrl(el, key, fn) {
  var self = this;
  key = /^\d{2,}$/.test(key) ? key : key.toUpperCase().charCodeAt(0);
  _bind(el, "keydown", function(e) {
    if (e.ctrlKey && e.which == key && !e.shiftKey && !e.altKey) {
      fn.call(el);
      e.stop();
    }
  });
}

var _readyFinished = false;

function _ready(fn) {
  if (_readyFinished) {
    fn(KindEditor);
    return;
  }
  var loaded = false;
  function readyFunc() {
    if (!loaded) {
      loaded = true;
      fn(KindEditor);
      _readyFinished = true;
    }
  }
  function ieReadyFunc() {
    if (!loaded) {
      try {
        document.documentElement.doScroll("left");
      } catch (e) {
        setTimeout(ieReadyFunc, 100);
        return;
      }
      readyFunc();
    }
  }
  function ieReadyStateFunc() {
    if (document.readyState === "complete") {
      readyFunc();
    }
  }
  if (document.addEventListener) {
    _bind(document, "DOMContentLoaded", readyFunc);
  } else if (document.attachEvent) {
    _bind(document, "readystatechange", ieReadyStateFunc);
    // 在跨域的frame里调用会报错
    var toplevel = false;
    try {
      toplevel = window.frameElement == null;
    } catch (e) {}
    if (document.documentElement.doScroll && toplevel) {
      ieReadyFunc();
    }
  }
  _bind(window, "load", readyFunc);
}

/**
	Note:
	发现绑定dbclick事件后移除element会有内存泄漏，以下代码也不起作用。
	Reference:
	http://isaacschlueter.com/2006/10/msie-memory-leaks/
	http://msdn.microsoft.com/en-us/library/bb250448.aspx
*/
if (window.attachEvent) {
  window.attachEvent("onunload", function() {
    _each(_eventData, function(key, events) {
      if (events.el) {
        _unbind(events.el);
      }
    });
  });
}

K.ctrl = _ctrl;
K.ready = _ready;
